/**
 * Copyright (C), 2017-2018, XXX有限公司
 * FileName: MockitoVisitor
 * Author:   zengjian
 * Date:     2018/8/16 13:57
 * Description: Mockito框架单元测试生成规则
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package org.yinxue.swing.unit.visitor;

import fun.codedesign.yinxue.util.StringUtil;
import org.yinxue.swing.unit.constant.ClassConstant;
import org.yinxue.swing.unit.constant.CommonType;
import org.yinxue.swing.unit.constant.UnitConstant;
import org.yinxue.swing.unit.context.Context;
import org.yinxue.swing.unit.model.ClassDesc;
import org.yinxue.swing.unit.model.FieldDesc;
import org.yinxue.swing.unit.model.MethodDesc;
import org.yinxue.swing.unit.model.StatementDesc;
import org.yinxue.swing.unit.util.UnitUtil;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 〈Mockito框架单元测试生成规则〉<br>
 * 〈一句话描述〉
 *
 * @author zengjian
 * @create 2018/8/16 13:57
 */
public class MockitoVisitor implements Visitor, ClassConstant {

    /**
     * 同一线程复用同一ClassUnitContext对象
     */
    private volatile ThreadLocal<ClassUnitContext> unitContextLocal = new ThreadLocal<>();


    @Override
    public String visit(ClassDesc classDesc) {
        String unitContext = null;
        if ((classDesc.modifier & ENUM) != 0) {
            unitContext = toBuildUnitContext(classDesc);
        } else if ((classDesc.modifier & GENERIC) != 0) {
            unitContext = toBuildUnitContext(classDesc);
        } else {
            // ignore 抽象类、注解、接口暂时忽略掉
        }
        return unitContext;
    }

    private String toBuildUnitContext(ClassDesc classDesc) {
        ClassUnitContext unitContext = getThreadUnitContext(classDesc);
        // 导入包调整为最后添加
        this.prepareImportLines(classDesc);
        this.doMakeUnitPackageLine(classDesc);
        this.doMakeUnitClassHeader(classDesc);
        this.doMakeUnitMethodLines(classDesc);
        this.doMakeUnitFieldLines(classDesc);
        this.doMakeUnitImportLines(classDesc);
        String unit = unitContext.unitPackageLine
                .append(unitContext.unitImportLines).append("\n")
                .append(unitContext.unitClassNameHeader)
                .append(unitContext.unitClassFields)
                .append(unitContext.unitMethodLines)
                .append(unitContext.unitClassNameFooter)
                .toString();
        // 清理StringBuilder，保证容器实例存在时，重复使用时为空
        unitContext.reset();
        return unit;
    }

    /**
     * 对导入包进行一次全量的匹配导入，将类似这种的java.util.*的导入包进行展开，添加到sourceImportLines中
     *
     * @param classDesc
     */
    private void prepareImportLines(ClassDesc classDesc) {
        Map<String, String> deployMap = new HashMap<>(32);
        Iterator<Map.Entry<String, String>> iterator = classDesc.sourceImportLines.entrySet().iterator();
        while (iterator.hasNext()) {
            String regexImportPath = iterator.next().getValue();
            Map<String, String> map = Context.register().getMatchRegetImportMap(regexImportPath);
            if (map != null) {
                deployMap.putAll(map);
            }
        }
        if (!deployMap.isEmpty()) {
            classDesc.sourceImportLines.putAll(deployMap);
        }
    }


    private void doMakeUnitPackageLine(ClassDesc classDesc) {
        classDesc.tempUnitContext.unitPackageLine.append(classDesc.packageLine + "\n\n");
    }


    private void doMakeUnitImportLines(ClassDesc classDesc) {
        for (String dynamicImportLine : classDesc.tempUnitContext.targetImportLines) {
            classDesc.tempUnitContext.unitImportLines.append(dynamicImportLine + "\n");
        }
    }

    private void doMakeUnitClassHeader(ClassDesc classDesc) {
        classDesc.tempUnitContext.unitClassNameHeader.append("public class " + classDesc.simpleName + "Test" + " {\n\n");
    }

    private void doMakeUnitFieldLines(ClassDesc classDesc) {
        if ((classDesc.modifier & POJO) != 0 || ((classDesc.modifier & UTIL) != 0)
                || (classDesc.modifier & ENUM) != 0 || classDesc.methodMap.isEmpty()) {
            return;
        }

        ClassUnitContext unitContext = classDesc.tempUnitContext;


        // 注入待测实体(待测实体是以无参构造进行构造的)
        unitContext.unitClassNameHeader.append(UnitConstant.SSSS).append(UnitConstant.INJECTMOCKS)
                .append(UnitConstant.SSSS).append("private ").append(classDesc.simpleName).append(UnitConstant.S).append(classDesc.defaultVariable).append(";\n\n");
        // 有inject就需要BeforeMethod
        unitContext.targetFieldLines.add(UnitConstant.BEFORE_METHOD_STATEMENT);

        // 按注解mock
        for (Map.Entry<String, FieldDesc> entry : classDesc.fieldMap.entrySet()) {
            String fieldAnnotation = entry.getValue().annotation;
            String fieldType = entry.getValue().type;
            String fieldVariable = entry.getKey();

            if (fieldAnnotation != null) {
                appendField4Mock(classDesc, unitContext, fieldType, fieldVariable);
            }
        }

        // 按需要添加beforemethod等项
        for (String targetFieldLine : unitContext.targetFieldLines) {
            System.out.println(targetFieldLine);
            unitContext.unitClassFields.append(targetFieldLine);
        }

        // mockito、testng导入包，可根据生成的单元测试不同进行置换
        unitContext.targetImportLines.add(UnitConstant.IMPORT_TESTNG_BEFORE_METHOD); // @BeforeMethod
        unitContext.targetImportLines.add(UnitConstant.IMPORT_MOCKITO); // @Mock @InjectMock MockitoAnnotations.initMocks(this)
    }

    private void appendField4Mock(ClassDesc classDesc, ClassUnitContext unitContext, String fieldType, String fieldVariable) {
        StringBuilder fieldBuilder = new StringBuilder();
        fieldBuilder.append(UnitConstant.SSSS).append("@Mock\n")
                .append(UnitConstant.SSSS).append("private ").append(fieldType).append(UnitConstant.S).append(fieldVariable).append(";\n\n");
        unitContext.targetFieldLines.add(fieldBuilder.toString());
        unitContext.targetFieldLines.add(UnitConstant.BEFORE_METHOD_STATEMENT);
        refreshTargetImportLines(fieldType, classDesc, unitContext);
    }

    private static void refreshTargetImportLines(String paramType, ClassDesc classDesc, ClassUnitContext unitContext) {
        if (StringUtil.isEmpty(paramType) || unitContext == null) {
            return;
        }
        // 原始类导入包中有相应的路径，即添加
        if (classDesc.sourceImportLines.containsKey(paramType)) {
            unitContext.targetImportLines.add(classDesc.sourceImportLines.get(paramType));
            return;
        }

        // 导入可能的初始化的类
        if (CommonType.getDeclaredClassImportPath(paramType) != null) {
            unitContext.targetImportLines.add("import " + CommonType.getDeclaredClassImportPath(paramType) + ";");
        }

        // 公共包，从容器中取初始化好的常用包
        String importPath = Context.register().getCommonImportPath(paramType);
        if (importPath != null) {
            unitContext.targetImportLines.add(importPath);
        }

        // 未导入的项目包，从容器中取
        ClassDesc classDesc1 = Context.register().getClassDesc(paramType);
        if (classDesc1 != null) {
            unitContext.targetImportLines.add(classDesc1.importPath);
        }
    }

    private void doMakeUnitMethodLines(ClassDesc classDesc) {
        ClassUnitContext unitContext = classDesc.tempUnitContext;
        if (!classDesc.methodMap.isEmpty()) {
            unitContext.targetImportLines.add(UnitConstant.IMPORT_TESTNG);
        }

        // 对pojo的单独处理
        if ((classDesc.modifier & POJO) != 0) {
            unitContext.unitMethodLines.append(UnitConstant.SSSS + "@Test\n").append(UnitConstant.SSSS + "public void testEntity() ");
            // 只要有一个方法抛异常就需要抛出异常
            appendExceptionIfExsit4Pojo(classDesc, unitContext);
            unitContext.unitMethodLines.append(appendConstrutorHeader4Pojo(classDesc, unitContext));
            // 说明有内部类，需要拼接header
            if (classDesc.hasNestClass()) {
                for (ClassDesc nestClassDesc : classDesc.nestClassList) {
                    unitContext.unitMethodLines.append(appendConstrutorHeader4Pojo(nestClassDesc, unitContext));
                }
                // 拼接内部类的get set等方法
                for (ClassDesc nestClassDesc : classDesc.nestClassList) {
                    unitContext.unitMethodLines.append(appendMethodBody4Pojo(nestClassDesc, unitContext));
                }
            }
            unitContext.unitMethodLines.append(appendMethodBody4Pojo(classDesc, unitContext));
            unitContext.unitMethodLines.append(UnitConstant.SSSS + "}\n");
            return;
        }
        // 测试方法去重复
        Map<String, Integer> uniqueMethodNameMap = new HashMap<>(48);
        for (Map.Entry<String, MethodDesc> methodEntry : classDesc.methodMap.entrySet()) {
            MethodDesc methodDesc = methodEntry.getValue();
            /* 由于可能存在方法重载的问题，所以需要对相同方法名称的情况，进行序号编号
               需要将对方法header的解析放在什么外部来解析 */
            String methodName = methodDesc.getMethodName();
            if (methodName == null) {
                continue;
            }
            String uniqueMethodName = methodNameMayChange(uniqueMethodNameMap, methodName);
            unitContext.unitMethodLines
                    .append(UnitConstant.SSSS).append("@Test\n")
                    .append(UnitConstant.SSSS).append("public void test").append(StringUtil.toUpperCaseFirstChar(uniqueMethodName)).append("() ");
            appendExceptionIfExsit(unitContext.unitMethodLines, methodDesc.exceptions, methodDesc);
            unitContext.unitMethodLines.append(this.visit(methodDesc));
            unitContext.unitMethodLines.append(UnitConstant.SSSS).append("}\n\n");
        }
    }

    // 方法名去重
    private String methodNameMayChange(Map<String, Integer> uniqueMethodNameMap, String methodName) {
        String uniqueMethodName = methodName;
        if (uniqueMethodNameMap.containsKey(methodName)) {
            Integer count = uniqueMethodNameMap.get(methodName);
            count++;
            uniqueMethodName += count;
            uniqueMethodNameMap.put(methodName, count);
        } else {
            uniqueMethodNameMap.put(methodName, 0);
        }
        return uniqueMethodName;
    }

    private void appendExceptionIfExsit4Pojo(ClassDesc classDesc, ClassUnitContext unitContext) {
        boolean exsitException = false;
        for (MethodDesc methodDesc : classDesc.methodMap.values()) {
            if (methodDesc.exceptions.size() > 0) {
                exsitException = true;
                break;
            }
        }

        if (exsitException == false) {
            unitContext.unitMethodLines.append("{\n");
            return;
        }
        unitContext.unitMethodLines.append("throws ");
        for (MethodDesc methodDesc : classDesc.methodMap.values()) {
            if (!methodDesc.exceptions.isEmpty()) {
                for (String exception : methodDesc.exceptions) {
                    unitContext.unitMethodLines.append(exception).append(",");
                    refreshTargetImportLines(exception, classDesc, unitContext);
                }
            }
        }
        // 前面已判断有Exception，所以直接将,进行替换即可
        unitContext.unitMethodLines.setCharAt(unitContext.unitMethodLines.lastIndexOf(","), ' ');
        unitContext.unitMethodLines.append("{\n");
    }

    static void appendExceptionIfExsit(StringBuilder unitMethodLines, List<String> exceptions, MethodDesc methodDesc) {
        if (!exceptions.isEmpty()) {
            unitMethodLines.append("throws ");
            Iterator<String> iterator = exceptions.iterator();
            while (iterator.hasNext()) {
                String exception = iterator.next();
                unitMethodLines.append(exception);
                refreshTargetImportLines(exception, methodDesc.parent, methodDesc.parent.tempUnitContext);
                if (iterator.hasNext()) {
                    unitMethodLines.append(",");
                }
            }
        }
        unitMethodLines.append("{\n");
    }

    private StringBuilder appendConstrutorHeader4Pojo(ClassDesc nestClassDesc, ClassUnitContext unitContext) {
        // 以下需要通过definition对象的parent来判断是否为子类还是主类，主类不加前缀，子类需要加主类前缀
        StringBuilder construtorBuilder = new StringBuilder(128);
        // 查看是否有有参构造器，将构造器按照
        Iterator<Map.Entry<String, MethodDesc>> iterator = nestClassDesc.constructrorMap.entrySet().iterator();
        int count = 0;
        while (iterator.hasNext()) {
            Map.Entry<String, MethodDesc> entry = iterator.next();
            MethodDesc constructor = entry.getValue();
            Map<String, String> params = UnitUtil.getMethodEntryParams(constructor.methodHeader);
            // 没入参默认为无参构造器，按默认变量拼接
            if (params == null || params.isEmpty()) {
                buildConstrutorPrefix(nestClassDesc, construtorBuilder, 0, params, unitContext);
                // 遍历取出属性，然后拼接一个有参对象
            } else {
                buildConstrutorPrefix(nestClassDesc, construtorBuilder, count, params, unitContext);
            }
            if (iterator.hasNext()) {
                count++;
            }
        }
        return construtorBuilder;
    }

    private void buildConstrutorPrefix(ClassDesc nestClassDesc, StringBuilder builder, int i, Map<String, String> params, ClassUnitContext unitContext) {
        builder.append(UnitConstant.SSSSSSSS + nestClassDesc.compositClassName + " " + nestClassDesc.defaultVariable + (i == 0 ? "" : i) + " = ");
        if ((nestClassDesc.modifier & ENUM) != 0) {
            builder.append(nestClassDesc.enumList.get(0)).append(";\n");
        } else {
            builder.append("new " + nestClassDesc.compositClassName + "(");
            appendParamsAndEnd(builder, params, nestClassDesc, unitContext);
        }
    }

    private StringBuilder appendMethodBody4Pojo(ClassDesc classDesc, ClassUnitContext unitContext) {
        StringBuilder testMethodBody = new StringBuilder(256);

        StringBuilder setMethodBody = new StringBuilder(128);
        StringBuilder getMethodBody = new StringBuilder(128);
        StringBuilder otherMethodBody = new StringBuilder(128);
        StringBuilder setGetDateNullBody = new StringBuilder(128);
        List<MethodDesc> setDateMethods = new ArrayList<>(6);

        for (MethodDesc methodDesc : classDesc.methodMap.values()) {
            String methodName = methodDesc.methodName;
            if (methodName.startsWith("set") || methodName.startsWith("is")) {
                setMethodBody.append(UnitConstant.SSSSSSSS).append(methodDesc.getParentVariable()).append(".").append(methodName).append("(");
                Iterator<Map.Entry<String, String>> iterator = methodDesc.params.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, String> entry = iterator.next();
                    String paramType = entry.getValue();
                    // 表示泛型
                    if (paramType.matches("^[A-Z][0-9]?$")) {
                        paramType = "Object";
                    }

                    String defaultParamValue = null;
                    // 处理内部类的情况
                    if (classDesc.hasNestClass()) {
                        // 有内部类先判断内部类的情况
                        for (int i = 0, size = classDesc.nestClassList.size(); i < size; i++) {
                            if (paramType.equals(classDesc.nestClassList.get(i).simpleName)) {
                                defaultParamValue = StringUtil.toLowerCaseFirstChar(paramType);
                                break;
                            }
                        }
                    }

                    // 如果没有匹配的内部类再按一般的类判断
                    if (defaultParamValue == null) {
                        defaultParamValue = CommonType.getInitParamValue(paramType);
                    }
                    // 按照实际初始的类导入包
                    String initParamType = CommonType.getRealInitClassName(paramType);
                    // 配合进入的是内部类的情况
                    ClassDesc classDesc1 = classDesc;
                    for (; (classDesc1.modifier & NEST) != 0; classDesc1 = classDesc1.parent) {
                    }
                    refreshTargetImportLines(initParamType, classDesc1, classDesc1.tempUnitContext);
                    // 解决 java.util.Date 和 java.sql.Date冲突问题
                    if ("new Date()".equals(defaultParamValue)) {
                        if (classDesc.sourceImportLines.get("Date").contains("java.sql.Date")) {
                            defaultParamValue = "new Date(System.currentTimeMillis())";
                            // 去除Date冲突
                            classDesc1.tempUnitContext.targetImportLines.remove("import java.util.Date;");
                        }
                    }
                    setMethodBody.append(defaultParamValue);

                    // 处理Date 和 Timestamp sonar覆盖的问题， 需要设置null然后再进行get，先缓存set的选项
                    if (("Date".equals(paramType) || "Timestamp".equals(paramType)) && methodDesc.params.size() == 1) {
                        setDateMethods.add(methodDesc);
                    }
                    if (iterator.hasNext()) {
                        setMethodBody.append(",");
                    }
                }
                setMethodBody.append(");\n");
            } else if (methodName.startsWith("get")) {
                getMethodBody.append(UnitConstant.SSSSSSSS + classDesc.defaultVariable).append(".")
                        .append(methodDesc.methodName).append("(");
                appendParamsAndEnd(getMethodBody, methodDesc.params, classDesc, unitContext);
            } else {
                otherMethodBody.append(UnitConstant.SSSSSSSS + classDesc.defaultVariable).append(".")
                        .append(methodDesc.methodName).append("(");
                appendParamsAndEnd(otherMethodBody, methodDesc.params, classDesc, unitContext);
            }
        }

        // 如果是包含了date 和 timestamp类型的值，需要对再set一次 null ，然后再get一次保证全覆盖
        /**
         *    public void setEndTime(Date endTime) {
         *        if (endTime != null) {
         *          this.endTime = new Date(endTime.getTime());
         *        } else {
         *           this.endTime = null;
         *       }
         *   }
         */
        if (!setDateMethods.isEmpty()) {
            for (MethodDesc setDateMethod : setDateMethods) {
                setGetDateNullBody.append(UnitConstant.SSSSSSSS + classDesc.defaultVariable).append(".")
                        .append(setDateMethod.methodName).append("(");
                setGetDateNullBody.append("null);\n");
                setGetDateNullBody.append(UnitConstant.SSSSSSSS).append(classDesc.defaultVariable).append(".");
                setGetDateNullBody.append("get" + setDateMethod.methodName.replace("set", ""));
                setGetDateNullBody.append("();\n");
            }
        }
        return testMethodBody.append(setMethodBody).append(getMethodBody).append(otherMethodBody).append(setGetDateNullBody);
    }

    private static void appendParamsAndEnd(StringBuilder body, Map<String, String> params, ClassDesc classDesc, ClassUnitContext unitContext) {
        if (params != null && !params.isEmpty()) {
            Iterator<Map.Entry<String, String>> iterator = params.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> entry = iterator.next();
                String paramType = entry.getValue();
                if (paramType != null) {
                    body.append(CommonType.getInitParamValue(paramType));
                    if (iterator.hasNext()) {
                        body.append(", ");
                    }
                }
                refreshTargetImportLines(paramType, classDesc, unitContext);
            }
        }
        body.append(");\n");
    }

    @Override
    public StringBuilder visit(MethodDesc methodDesc) {
        buildRealMethodInvokeLines(methodDesc);
        buildMockMethodInvokeLines(methodDesc);
        return methodDesc.unitVariableLines.append(methodDesc.unitMockMethodLines).append(methodDesc.unitInvokeLines);
    }


    private void buildRealMethodInvokeLines(MethodDesc methodDesc) {
        // 静态方法采用类本身，不用变量
        if ((methodDesc.modifier & STATIC) != 0) {
            methodDesc.unitInvokeLines.append(UnitConstant.SSSSSSSS).append(methodDesc.getParentName()).append(".").append(methodDesc.methodName).append("(");
            refreshTargetImportLines(methodDesc.getParentName(), methodDesc.parent, methodDesc.parent.tempUnitContext);
        } else if (methodDesc.isEnumMethod()) {
            methodDesc.unitInvokeLines.append(UnitConstant.SSSSSSSS).append(methodDesc.getDefaultEnumName()).append(".").append(methodDesc.methodName).append("(");
        } else {
            methodDesc.unitInvokeLines.append(UnitConstant.SSSSSSSS).append(methodDesc.getParentVariable()).append(".").append(methodDesc.methodName).append("(");
        }
        Iterator<Map.Entry<String, String>> iterator = methodDesc.params.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            String paramVariable = entry.getKey();
            String paramType = entry.getValue();
            String uniqueParamVar = uniqueParamVariable(methodDesc, paramVariable);
            methodDesc.unitInvokeLines.append(uniqueParamVar);
            // 如果是加长数组,将该参数类型转换标准的数组格式
            paramType = convertParamTypeIfNeed(paramType);
            String paramInitValue = getParamInitValue(paramType, methodDesc);
            methodDesc.unitVariableLines.append(UnitConstant.SSSSSSSS).append(paramType + " " + uniqueParamVar + " = ");
            // 内部已有后缀;\n
            methodDesc.unitVariableLines.append(paramInitValue);
            if (iterator.hasNext()) {
                methodDesc.unitInvokeLines.append(", ");
            }
        }
        methodDesc.unitInvokeLines.append(");\n");
    }

    private String uniqueParamVariable(MethodDesc methodDesc, String paramVar) {
        String uniqueParamVar = paramVar;
        if (methodDesc.unitVariableTable.containsKey(paramVar)) {
            int count = methodDesc.unitVariableTable.get(paramVar);
            uniqueParamVar = paramVar + (++count);
            methodDesc.unitVariableTable.put(paramVar, count);
        } else {
            methodDesc.unitVariableTable.put(paramVar, 0);
        }
        return uniqueParamVar;
    }

    private void buildMockMethodInvokeLines(MethodDesc methodDesc) {
        // TODO 疑似重复判断 先刷新返回类型及返回值，及返回变量去重复
        methodDesc.refreshReturnParam();
        for (StatementDesc statement : methodDesc.statements) {
            // 此处拦截下，过滤大写开头的mock方法
            if (statement.methodVariable.substring(0, 1).matches("[A-Z]")) {
                continue;
            }
            StatementUnitContext statementUnitContext = this.visit(statement);
            methodDesc.unitVariableLines.append(statementUnitContext.variableBuilder);
            methodDesc.unitMockMethodLines.append(statementUnitContext.invokeBuilder);
        }
    }

    private String convertParamTypeIfNeed(String paramType) {
        if (paramType.endsWith("...")) {
            paramType = paramType.replaceAll("\\.\\.\\.", "[]");
        }
        // 如果是泛型，参数类型转换为Object
        if (paramType.matches("^[A-Z][0-9]?$")) {
            paramType = "Object";
        }
        return paramType;
    }

    public static String getParamInitValue(String paramType, MethodDesc methodDesc) {
        String paramInitValue = "";
        String fullClassName = UnitUtil.fullClassName(paramType, methodDesc.parent);
        ClassUnitContext unitContext = methodDesc.parent.tempUnitContext;
        if (CommonType.getInitParamValue(paramType) != null) {
            // 此处ParamType和初始值类型可能不同，所以需要根据实际的初始值刷新下导入包
            paramInitValue = CommonType.getInitParamValue(paramType) + ";\n";
            String paramInitType = CommonType.getRealInitClassName(paramType);
            refreshTargetImportLines(paramInitType, methodDesc.parent, unitContext);
        } else if (paramType.matches(UnitConstant.NEED_MOCK_CLASSDESC)) {
            paramInitValue = "mock(" + paramType + ".class);\n";
            unitContext.targetImportLines.add(UnitConstant.IMPORT_MOCKITO_MOCKITO_STATIC);
        } else if (getClassDescFromContext(fullClassName) != null) {
            // 加入容器判断
            ClassDesc cacheClassDesc = getClassDescFromContext(fullClassName);
            int modifier = cacheClassDesc.modifier;
            if ((modifier & ENUM) != 0) {
                if (cacheClassDesc.hasEnumList()) {
                    paramInitValue = cacheClassDesc.getDefaultEnum() + ";\n";
                }
            } else if ((modifier & INTERFACE) != 0 || (modifier & ABSTRACT) != 0 || (modifier & ANNOTATION) != 0) {
                paramInitValue = "mock(" + cacheClassDesc.simpleName + ".class);\n";
                unitContext.targetImportLines.add(UnitConstant.IMPORT_MOCKITO_MOCKITO_STATIC);
            } else {
                for (MethodDesc construct : cacheClassDesc.constructrorMap.values()) {
                    // 添加构造器参数
                    paramInitValue = buildNewInitValue(construct);
                    break;
                }
            }
        } else {
            paramInitValue = "mock(" + paramType + ".class);\n";
            unitContext.targetImportLines.add(UnitConstant.IMPORT_MOCKITO_MOCKITO_STATIC);
        }
        refreshTargetImportLines(paramType, methodDesc.parent, unitContext);
        return paramInitValue;
    }

    private static ClassDesc getClassDescFromContext(String paramType) {
        return Context.register().getClassDesc(paramType);
    }

    private static String buildNewInitValue(MethodDesc construct) {
        StringBuilder initNewValue = new StringBuilder(" new " + construct.methodName + "(");
        appendParamsAndEnd(initNewValue, construct.params, construct.parent, construct.parent.tempUnitContext);
        return initNewValue.toString();
    }

    @Override
    public StatementUnitContext visit(StatementDesc statementDesc) {
        StringBuilder mockMethodVariableBuilder = buildMockMethodVariable(statementDesc, statementDesc.parent.tempUnitContext);
        StringBuilder mockMethodInvokeBuilder = buildMockMethodInvoke(statementDesc);
        return new StatementUnitContext(mockMethodVariableBuilder, mockMethodInvokeBuilder);
    }

    private StringBuilder buildMockMethodVariable(StatementDesc statement, ClassUnitContext unitContext) {
        StringBuilder builder = new StringBuilder(255);

        // 声明返回变量
        if (StringUtil.isNotEmpty(statement.returnType) && !statement.returnType.equals("void")) {
            String uniqueReturnParam = paramVarMayChange(statement.returnParam, statement);
            builder.append(UnitConstant.SSSSSSSS).append(statement.returnType).append(" ").append(uniqueReturnParam)
                    .append(" = ").append(statement.returnInitValue);
        }

        // 如果mock中没有语句调用的变量，那么需要自己再mock下该语句，先用变量找到类型，再拼接mock HACK 可以优化
        if (isNotMatchFieldMap(statement, statement.methodVariable)) {
            // 根据变量在全文中找对应的类型
            String paramType = findVariableTypeByContext(statement.methodVariable, statement);
            if (paramType != null) {
                appendField4Mock(statement.parent, unitContext, paramType, statement.methodVariable);
            }
        }
        return builder;
    }

    private boolean isNotMatchFieldMap(StatementDesc statement, String variable) {
        return !statement.parent.fieldMap.containsKey(variable) || statement.parent.fieldMap.get(variable).annotation == null;
    }

    private String findVariableTypeByContext(String methodVariable, StatementDesc statement) {
        String context = statement.methodContext;
        Pattern pattern = Pattern.compile("[;\n \\}\\{]([A-Za-z0-9_]+) +(" + methodVariable + ");");
        Matcher matcher = pattern.matcher(context);
        while (matcher.find()) {
            return matcher.group(1);
        }

        matcher = pattern.matcher(statement.parent.context);
        while (matcher.find()) {
            return matcher.group(1);
        }

        Collection<ClassDesc> superRefs = statement.parent.superClassRef.values();
        if (superRefs != null) {
            for (ClassDesc superRef : superRefs) {
                matcher = pattern.matcher(superRef.context);
                while (matcher.find()) {
                    return matcher.group(1);
                }
            }
        }
        return null;
    }

    private String paramVarMayChange(String returnParam, StatementDesc statementDesc) {
        String uniqueReturnParam = returnParam;
        if (statementDesc.parentMethodDesc.unitVariableTable.containsKey(returnParam)) {
            int count = statementDesc.parentMethodDesc.unitVariableTable.get(returnParam);
            count++;
            uniqueReturnParam += count;
            statementDesc.parentMethodDesc.unitVariableTable.put(returnParam, count);
        } else {
            statementDesc.parentMethodDesc.unitVariableTable.put(returnParam, 0);
        }
        statementDesc.returnParam = uniqueReturnParam;
        return uniqueReturnParam;
    }

    private StringBuilder buildMockMethodInvoke(StatementDesc statementDesc) {
        // 如果为空，就进行两种可能性拼接，一种是doNothing，一种是return null，如果有确定的值，就进行一种拼接
        StringBuilder invokeMethodMock = new StringBuilder(256);
        String type = statementDesc.returnType;
        if (type != null) {
            invokeMethodMock.append(UnitConstant.SSSSSSSS);
            if (type.equals("void")) {
                buildStatement4MockDoNothing(statementDesc, invokeMethodMock);
            } else {
                buildStatement4MockHaveReturn(statementDesc, invokeMethodMock);
            }
        }
        return invokeMethodMock;
    }

    private void buildStatement4MockDoNothing(StatementDesc statementDesc, StringBuilder mockMethod) {
        mockMethod.append("doNothing().when(")
                .append(statementDesc.methodVariable)
                .append(").")
                .append(statementDesc.methodName)
                .append("(");
        addEntryParams(statementDesc, mockMethod);
        mockMethod.append(";\n");
        statementDesc.parent.tempUnitContext.targetImportLines.add(UnitConstant.IMPORT_MOCKITO_MOCKITO_STATIC);
        statementDesc.parent.tempUnitContext.targetImportLines.add(UnitConstant.IMPORT_MOCKITO_MATCHER_STATIC);
    }

    private void buildStatement4MockHaveReturn(StatementDesc statementDesc, StringBuilder mockMethod) {
        mockMethod.append("when(")
                .append(statementDesc.methodVariable)
                .append(".")
                .append(statementDesc.methodName)
                .append("(");
        addEntryParams(statementDesc, mockMethod);
        mockMethod.append(").thenReturn(");
        mockMethod.append(statementDesc.returnParam);
        mockMethod.append(");\n");
        statementDesc.parent.tempUnitContext.targetImportLines.add(UnitConstant.IMPORT_MOCKITO_MOCKITO_STATIC);
        statementDesc.parent.tempUnitContext.targetImportLines.add(UnitConstant.IMPORT_MOCKITO_MATCHER_STATIC);
    }

    private void buildMockWordForTwoCases(StatementDesc statementDesc, StringBuilder mockMethod) {
        // 情况一: doNothing().when()
        mockMethod.append("doNothing().when(")
                .append(statementDesc.methodVariable)
                .append(").")
                .append(statementDesc.methodName)
                .append("(");
        addEntryParams(statementDesc, mockMethod);
        mockMethod.append(";\n");

        // 情况二: when() 默认返回null
        mockMethod.append("when(")
                .append(statementDesc.methodVariable)
                .append(".")
                .append(statementDesc.methodName)
                .append("(");
        addEntryParams(statementDesc, mockMethod);
        mockMethod.append(").thenReturn(null);\n");
        statementDesc.parent.tempUnitContext.targetImportLines.add(UnitConstant.IMPORT_MOCKITO_MOCKITO_STATIC);
        statementDesc.parent.tempUnitContext.targetImportLines.add(UnitConstant.IMPORT_MOCKITO_MATCHER_STATIC);
    }

    private void addEntryParams(StatementDesc statmentDesc, StringBuilder mockInvokeMethod) {
        // 增加参数前采用容器进行下检查
        for (Map.Entry<String, String> entry : statmentDesc.params.entrySet()) {
            // key 变量  value 类别
            if (entry.getValue().equals(UnitConstant.PLACE_HOLDER)) {
                String fullClassName = UnitUtil.fullClassName(statmentDesc.methodClassName, statmentDesc.parent);
                ClassDesc cacheClassDesc = Context.register().getClassDesc(fullClassName);
                // 在本身类去找，再到继承类去找
                findParamTypeFromContext(statmentDesc, cacheClassDesc, statmentDesc.methodName);
            }
        }

        Iterator<Map.Entry<String, String>> iterator = statmentDesc.params.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entryParam = iterator.next();
            String paramType = entryParam.getValue();
            mockInvokeMethod.append("any(")
                    .append(entryParam.getValue())
                    .append(".class")
                    .append(")");
            if (iterator.hasNext()) {
                mockInvokeMethod.append(", ");
            }
            refreshTargetImportLines(paramType, statmentDesc.parent, statmentDesc.parent.tempUnitContext);
        }
        mockInvokeMethod.append(")");
    }

    /**
     * 递归获取方法的入参 <br>
     *
     * @param statementDesc
     * @param classDesc
     * @param methodName
     */
    private void findParamTypeFromContext(StatementDesc statementDesc, ClassDesc classDesc, String methodName) {
        if (classDesc != null) {
            Map<String, MethodDesc> map = classDesc.methodMap;
            for (Map.Entry<String, MethodDesc> methodEntry : map.entrySet()) {
                if (methodName.equals(methodEntry.getValue().methodName)) {
                    statementDesc.params = new LinkedHashMap<>(methodEntry.getValue().params);
                    // 如果存在的是泛型，那么就从childMethod对应的definetion中的泛型替换为其中记录的真实泛型
                    for (Map.Entry<String, String> entry : statementDesc.params.entrySet()) {
                        // 如果类型是泛型，即大写的字母，则将该泛型更新为childMethodName中对应的该类的对应的泛型替换
                        int i = 0;
                        if (entry.getValue().matches("[A-Z]")) {
                            ClassDesc cacheClassDesc = Context.register().getClassDesc(UnitUtil.fullClassName(statementDesc.methodClassName, statementDesc.parent));
                            List<String> list = cacheClassDesc.genericType.get(classDesc.simpleName);
                            statementDesc.params.put(entry.getKey(), list.get(i));
                            i++;
                        }
                    }
                    return;
                }
            }
            // 没找到再继续找它的父类
            if (classDesc.hasSuperClass()) {
                Map<String, ClassDesc> superMap = classDesc.superClassRef;
                for (Map.Entry<String, ClassDesc> entry : superMap.entrySet()) {
                    ClassDesc superClassDesc = entry.getValue();
                    if (superClassDesc == null) {
                        superClassDesc = Context.register().getClassDesc(entry.getKey());
                    }
                    if (superClassDesc == null) {
                        return;
                    }
                    findParamTypeFromContext(statementDesc, superClassDesc, methodName);
                }
            }
        }
    }

    @Override
    public void visit(FieldDesc fieldDesc) {

    }

    ClassUnitContext getThreadUnitContext(ClassDesc classDesc) {
        if (unitContextLocal.get() == null) {
            unitContextLocal.set(new ClassUnitContext());
        }
        return unitContextLocal.get().setClassDesc(classDesc);
    }
}